package demo.servlet;

import java.io.File;
//import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.FileUtils;
import org.eclipse.jgit.api.Git;

import demo.output.Detail;
import demo.util.AntUtil;
import demo.util.CodeMessageUtil;
import demo.util.GitUtil;

/**
 * Servlet implementation class TaskEvaServlet
 */
@WebServlet("/TaskEvaServlet")
public class TaskEvaServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private String allCodeUrl = "https://git.oschina.net/qingkeme/javatraining.git";
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public TaskEvaServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
//		response.getWriter().append("Served at: ").append(request.getContextPath());
		String gameId = request.getParameter("courseCode");
		String taskId = request.getParameter("taskCode");
		String url = request.getParameter("url");
		Detail detail = new Detail();
		//clone qk code first
		String allCodePath = cloneGit(allCodeUrl);
		if ("".equals(allCodePath)) {
			detail.setCode(CodeMessageUtil.CD_502);
			detail.setMessage("请联系管理员，练习库clone失败");
			doResponse(response, detail);
			return;
		}
		//check game and task folder
		String taskCodePath = checkGameTaskFolder(gameId, taskId, allCodePath);
		if("".equals(taskCodePath)) {
			detail.setCode(CodeMessageUtil.CD_200);
			detail.setMessage("练习库里面暂时还没有该课时的练习代码");
			doResponse(response, detail);
			FileUtils.deleteDirectory(new File(allCodePath));
			return;
		}
		//clone user git code
		url = GitUtil.wrapGitUrl(url);
		String userCodePath = cloneGit(url);
		if ("".equals(userCodePath)) {
			detail.setCode(CodeMessageUtil.CD_502);
			detail.setMessage("clone用户的git库失败");
			doResponse(response, detail);
			FileUtils.deleteDirectory(new File(allCodePath));
			return;
		}
		//check game and task in user code
		String userTaskCodePath = checkGameTaskFolder(gameId, taskId, userCodePath);
		if("".equals(userTaskCodePath)) {
			detail.setCode(CodeMessageUtil.CD_401);
			detail.setMessage("学生应该是忘记push他们的代码吧");
			doResponse(response, detail);
			FileUtils.deleteDirectory(new File(allCodePath));
			FileUtils.deleteDirectory(new File(userCodePath));
			return;
		}
		//merge the code
		String mergedCode = mergeTheCode(taskCodePath, userTaskCodePath);
		if("".equals(mergedCode)) {
			detail.setCode(CodeMessageUtil.CD_503);
			detail.setMessage("something wrong when merge the code.");
			doResponse(response, detail);
			FileUtils.deleteDirectory(new File(allCodePath));
			FileUtils.deleteDirectory(new File(userCodePath));
			return;
		}
		//generate build.xml for ant
		generateXml(mergedCode);
		AntUtil ant = new AntUtil();
		String antResult = ant.run(mergedCode + File.separator + "build.xml");
		if (antResult.indexOf("BUILD SUCCESSFUL") < 0) {
			detail.setCode(CodeMessageUtil.CD_504);
			detail.setMessage("ant build fail");
			detail.setDetail(antResult);
			doResponse(response, detail);
			FileUtils.deleteDirectory(new File(allCodePath));
			FileUtils.deleteDirectory(new File(userCodePath));
//			FileUtils.deleteDirectory(new File(mergedCode));
			return;
		}
		//collect junit result
		int fe = ant.getFailureAndError();
		if (fe == 0) {
			detail.setCode(CodeMessageUtil.CD_200);
			detail.setMessage("GOOD");
		} else {
			detail.setCode(CodeMessageUtil.CD_505);
			detail.setMessage("Junit run fail or error");
		}
		detail.setDetail("");
		File reportDir = new File(mergedCode + File.separator + "rpt");
		File[] reports = reportDir.listFiles();
		for(File reportFile : reports) {
			if(reportFile.isFile()) {
				String content = FileUtils.readFileToString(reportFile, "UTF-8");
				detail.setDetail(detail.getDetail() + "###" + content);
			}
		}
		doResponse(response, detail);
		//finally we need to do the cleanup if this poc is good
		FileUtils.deleteDirectory(new File(allCodePath));
		FileUtils.deleteDirectory(new File(userCodePath));
		FileUtils.deleteDirectory(new File(mergedCode));
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
	
	private void doResponse(HttpServletResponse response, Detail detail) throws IOException {
		response.setCharacterEncoding("UTF-8");
		response.setContentType("application/json");
		response.getWriter().print(detail.toJson());
	}
	
	private String cloneGit(String url) {
//		String tmpdir = System.getProperty("java.io.tmpdir");
//		tmpdir += File.separator + random();
//		File localPath = new File(tmpdir);
//		localPath.mkdir();
		File localPath = new File(GitUtil.getTempFolder());
		System.out.println("TempFolder:" + localPath.getAbsolutePath());
		try {
			Git result = Git.cloneRepository()
	                .setURI(url)
	                .setDirectory(localPath)
	                .call();
			System.out.println("GIT:" + url);
			System.out.println("GIT:" + result.getRepository().getDirectory());
			String ret = result.getRepository().getDirectory().getParent();
			System.out.println("GIT:" + ret);
			result.close();
			return ret;
		} catch (Exception e) {
			System.err.println("Error:" + e.getMessage());
			return "";
		}
	}
	
	private String random() {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
		return sdf.format(new Date());
	}
	
	private String checkGameTaskFolder(String gameId, String taskId, String codePath) {
//		String combined = "task" + taskId;
		String fullpath = codePath + File.separator + gameId + File.separator + taskId
				+ File.separator + "taskcode";
		File file = new File(fullpath);
		if (file.isDirectory() && file.exists() && file.canRead()) {
			return file.getAbsolutePath();
		}
		return "";
	}
	
	private String mergeTheCode(String taskCodePath, String userCodePath)  {
//		String tmpdir = System.getProperty("java.io.tmpdir");
//		tmpdir += File.separator + random();
//		File localPath = new File(tmpdir);
//		localPath.mkdir();
		File localPath = new File(GitUtil.getTempFolder());
		System.out.println("TempFolder:" + localPath.getAbsolutePath());
		//copy all files under test and lib folder
		try {
			FileUtils.copyDirectory(new File(taskCodePath + File.separator + "test"), 
					new File(localPath.getAbsolutePath() + File.separator + "test"));
			FileUtils.copyDirectory(new File(taskCodePath + File.separator + "lib"), 
					new File(localPath.getAbsolutePath() + File.separator + "lib"));
			//copy all files under src 
			FileUtils.copyDirectory(new File(userCodePath + File.separator + "src"), 
					new File(localPath.getAbsolutePath() + File.separator + "src"));
		} catch (Exception e) {
			System.err.println("Error: merge the code " + e.getMessage());
			return "";
		}
		return localPath.getAbsolutePath();
	}
	
	private void generateXml(String codePath) throws IOException {
		File lib = new File(codePath + File.separator + "lib");
		File[] libFiles = lib.listFiles();
		List<String> jarFiles = new ArrayList<String>();
		for(File f : libFiles) {
			if(f.isFile() && f.getName().endsWith(".jar")) {
				jarFiles.add(f.getName());
			}
		}
		File xml = new File(codePath + File.separator + "build.xml");
		FileWriter fw = new FileWriter(xml);
		PrintWriter pw = new PrintWriter(fw);
		pw.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
		pw.println("<project name=\"demo\" default=\"all\">");
		pw.println("<target name=\"all\">");
		pw.println("<delete dir=\"bin\" /><delete dir=\"tbin\" /><delete dir=\"rpt\"/>");
		pw.println("<mkdir dir=\"bin\" /><mkdir dir=\"tbin\" /><mkdir dir=\"rpt\"/>");
		pw.println("<javac srcdir=\"src\" destdir=\"bin\"><classpath>");
		pw.println("<pathelement path=\".\" />");
		for(String jar : jarFiles) {
			pw.println("<pathelement path=\"lib/" + jar + "\" />");
		}
		pw.println("</classpath></javac>");
		pw.println("<javac srcdir=\"test\" destdir=\"tbin\"><classpath>");
		pw.println("<pathelement path=\"bin\" />");
		for(String jar : jarFiles) {
			pw.println("<pathelement path=\"lib/" + jar + "\" />");
		}
		pw.println("</classpath></javac>");
		pw.println("<junit printsummary=\"withOutAndErr\" fork=\"yes\" timeout=\"60000\">");
		pw.println("<classpath><pathelement path=\"bin\" />");
		pw.println("<pathelement path=\"tbin\" />");
		for(String jar : jarFiles) {
			pw.println("<pathelement path=\"lib/" + jar + "\" />");
		}
		pw.println("</classpath><formatter type=\"xml\" />");
		pw.println("<batchtest todir=\"rpt\">");
		pw.println("<fileset dir=\"test\"><include name=\"**/*Test*.java\" /></fileset>");
		pw.println("</batchtest></junit>");
		pw.println("</target></project>");
		pw.close();
	}

}
